﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SPVisualDev.VSAddin.Dialogs;
using System.Windows.Forms;
using EnvDTE80;
using EnvDTE;
using SPVisualDev.Services;
using System.Runtime.InteropServices;
using SPVisualDev.VSAddin.Services;

namespace SPVisualDev.VSAddin
{
    internal class DebugHelper
    {
        private DTE2 m_dte;
        private System.Timers.Timer _debug_launch_delay_timer;
        private string _debug_launch_target_url;
        private IMainService _debug_launch_service_proxy;
        private int _debug_launch_attempt;
        private int _debug_browser_process_id;
        private int _debug_wp_process_id;
        private string _debug_transport;
        private SpecialProject _debug_project;
        private DialogDebugWatcher _dlg_watcher;

        [DllImport("user32.dll")]
        static extern bool SetForegroundWindow(IntPtr hwnd);

        public DebugHelper(DTE2 dte)
        {
            m_dte = dte;
        }

        public string OpenDebugTargetDialog(SpecialProject specialProject, bool bShowOpenInNewBrowserCheckbox)
        {
            string debugTargetUrl = specialProject.GetUserSettingsValue("DebugTargetURL");

            using (DialogDebug dialog = new DialogDebug(specialProject))
            {
                List<string[]> listToShow = new List<string[]>();
                List<IExplorerObjectContainer> listOfwebApps = specialProject.ServiceProxy.GetChildren(new ExFarm(), "Web applications");
                foreach (ExWebApplication webapp in listOfwebApps)
                {
                    listToShow.Add(new string[] { webapp.Title, webapp.Url, "True" });
                }

                dialog.txtByPage.Text = debugTargetUrl;
                dialog.WebApplicationsList = listToShow;
                dialog.WebApplicationsListView.CheckBoxes = false;
                dialog.WebApplicationsListView.MultiSelect = false;

                if (!bShowOpenInNewBrowserCheckbox)
                    dialog.chkOpenNewBrowser.Visible = false;

                DialogResult result = dialog.ShowDialog();
                if (result != DialogResult.OK)
                    return null;

                debugTargetUrl = null;

                if (dialog.rdoByPageUrl.Checked)
                {
                    debugTargetUrl = dialog.txtByPage.Text;
                }
                else if (dialog.rdoByWebApplications.Checked)
                {
                    if (dialog.WebApplicationsListView.SelectedItems.Count == 0)
                        throw new Exception("No web application selected.");

                    debugTargetUrl = dialog.WebApplicationsListView.SelectedItems[0].SubItems[1].Text;
                }
                else
                {
                    throw new NotImplementedException();
                }

                specialProject.SetUserSettingsValue("DebugTargetURL", debugTargetUrl, true);

                return debugTargetUrl;
            }

        }

        public void StopDebugger()
        {
            m_dte.Debugger.Stop(true);
        }

        public void AttachDebuggerToSPVisualDevService(SpecialProject specialProject)
        {
            Debugger2 dbg2 = m_dte.Debugger as Debugger2;
            Transport trans = dbg2.Transports.Item("Default");
            Engine engine = trans.Engines.Item("Managed");

            int debugProcessPID = 0;

            string server = "localhost";
            if (specialProject != null && specialProject.UseService)
            {
                if (!string.IsNullOrEmpty(specialProject.RemoteConnection.Address))
                    server = specialProject.RemoteConnection.Address;

                debugProcessPID = specialProject.ServiceProxy.GetServicePID();
            }
            else
            {
                ConnectionDetailsStruct connectionDetails = new ConnectionDetailsStruct();
                connectionDetails.Address = server;
                connectionDetails.Port = Common.C_WCF_MAIN_SVC_PORT;

                //try
                //{
                    using (MainServiceWrapper svc = new MainServiceWrapper(connectionDetails))
                    {
                        debugProcessPID = svc.ServiceProxy.GetServicePID();
                    }
                //}
                //catch (Exception ex)
                //{
                //    DialogError.ShowError(ex, "Communication error with SPVisualDev Windows Service.", false);
                //}
            }

            Processes processes = dbg2.GetProcesses(trans, server);

            if (processes.Count == 0)
            {
                throw new Exception("No processes could be found on server " + server + ". This could be due to a logon failure.");
            }

            foreach (Process2 p in processes)
            {
                if (p.ProcessID == debugProcessPID)
                {
                    if (p.IsBeingDebugged)
                    {
                        throw new Exception("The process is already being debugged.");
                    }
                    else
                    {
                        try
                        {
                            p.Attach2(engine);
                        }
                        catch(Exception ex)
                        {
                            DialogError.ShowError(ex, "Failed to attach debugger. For remote debugging please read the msdn article http://msdn.microsoft.com/en-us/library/y7f5zaaa.aspx.", false);
                        }
                    }

                    return;
                }
            }
        }

        public void AttachDebugger(SpecialProject specialProject, bool bForceShowUI)
        {
            if (m_dte.Debugger.CurrentMode != dbgDebugMode.dbgDesignMode)
                throw new Exception("The IDE is already in debug mode.");

            
            string target_url = specialProject.GetUserSettingsValue("DebugTargetURL");
            if (string.IsNullOrEmpty(target_url) || bForceShowUI)
            {
                target_url = OpenDebugTargetDialog(specialProject, true);

                if (target_url == null)
                    return;
            }

            if (string.IsNullOrEmpty(target_url) || !target_url.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
            {
                throw new Exception("No target selected.");
            }

            AttachDebugger(specialProject.ServiceProxy, specialProject, target_url, false);
        }

        public void AttachDebugger(SpecialProject specialProject, ProjectItem pageItem, string BaseURL)
        {
            if (m_dte.Debugger.CurrentMode != dbgDebugMode.dbgDesignMode)
                throw new Exception("The IDE is already in debug mode.");

            string target_url = null;
            if (pageItem != null)
            {
                string path = Common.GetProjectItemPath(pageItem, true);
                if (path.IndexOf("12\\TEMPLATE\\LAYOUTS", StringComparison.OrdinalIgnoreCase) != -1)
                {
                    target_url = BaseURL + "/_layouts" + path.Substring(19).Replace("\\", "/");

                    if (target_url.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) ||
                        target_url.EndsWith(".vb", StringComparison.OrdinalIgnoreCase))
                    {
                        target_url = target_url.Substring(0, target_url.Length - 3);
                    }
                }
            }

            if (pageItem == null || target_url == null)
            {
                throw new Exception("No valid page is active in the editor. Only application pages under '/_layouts' catalog are supported for debugging.");
            }
            else
            {
                //if (specialProject != null)
                //    specialProject.SetUserSettingsValue("DebugTargetURL", target_url, true);

                AttachDebugger(specialProject.ServiceProxy, specialProject, target_url, true);
            }
        }

        private bool ShowDebugTransportSelectorDialog()
        {
            using (DialogDebugTransportSelector dialog = new DialogDebugTransportSelector(m_dte))
            {
                DialogResult result = dialog.ShowDialog();

                return (result == DialogResult.OK);
            }
        }

        public void AttachDebugger(IMainService serviceProxy, SpecialProject specialProject, string Url, bool bIgnoreOpenInNewBrowserFlag)
        {
            if (m_dte.Debugger.CurrentMode != dbgDebugMode.dbgDesignMode)
                throw new Exception("The IDE is already in debug mode.");

            _debug_transport = Common.GetUserRegistryParam("DefaultDebugTransport", false);
            if (string.IsNullOrEmpty(_debug_transport))
            {
                bool result =ShowDebugTransportSelectorDialog();
                if (!result)
                    return;
            }

            bool bReuseBrowser = false;
            if (_debug_launch_target_url != null && _debug_launch_target_url.Equals(Url, StringComparison.OrdinalIgnoreCase))
                bReuseBrowser = true;

            _debug_launch_target_url = Url;


            if (_debug_launch_delay_timer != null)
                _debug_launch_delay_timer.Dispose();

            _debug_wp_process_id = 0;
            _debug_launch_attempt = 0;
            _debug_wp_process_id = -1;
            _debug_launch_service_proxy = serviceProxy;
            _debug_project = specialProject;

            _debug_launch_delay_timer = new System.Timers.Timer();
            _debug_launch_delay_timer.AutoReset = false;
            _debug_launch_delay_timer.Interval = 500;
            _debug_launch_delay_timer.Elapsed += new System.Timers.ElapsedEventHandler(_debug_launch_delay_timer_Elapsed);

            _debug_launch_delay_timer_Elapsed(_debug_launch_delay_timer, null);

            if (_debug_wp_process_id == -1)
            {
                if (_dlg_watcher != null)
                    _dlg_watcher.Dispose();

                _dlg_watcher = new DialogDebugWatcher();
                _dlg_watcher.InfoMessage = "Waiting for the worker process for web application '" + _debug_launch_target_url + "' to start. Refresh your browser session to start the worker process.";
                _dlg_watcher.FormClosed += new FormClosedEventHandler(_dlg_watcher_FormClosed);
                _dlg_watcher.Show();

                _debug_launch_delay_timer.Start();
            }


            System.Diagnostics.Process p = null;
            try
            {
                if (_debug_browser_process_id != 0)
                {
                    try
                    {
                        p = System.Diagnostics.Process.GetProcessById(_debug_browser_process_id);
                    }
                    catch
                    {
                    }

                    if (p != null && bReuseBrowser)
                    {
                        try
                        {
                            SetForegroundWindow(p.MainWindowHandle);
                        }
                        catch
                        {
                        }
                    }
                }

                if (!bIgnoreOpenInNewBrowserFlag)
                {
                    string s = Common.GetUserRegistryParam("DebugOpenInNewBrowserWindow", true);
                    if (!string.IsNullOrEmpty(s))
                    {
                        bool b = bool.Parse(s);
                        if (b)
                        {
                            bReuseBrowser = false;
                        }
                        else
                        {
                            return;
                        }
                    }
                }

                if (p == null || !bReuseBrowser)
                {
                    _debug_browser_process_id = Common.LaunchBrowser(_debug_launch_target_url);
                }
            }
            finally
            {
                if (p != null)
                    p.Dispose();
            }
        }

        private void _dlg_watcher_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (_debug_launch_delay_timer != null)
                _debug_launch_delay_timer.Stop();
        }


        private void _debug_launch_delay_timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {

                _debug_launch_attempt++;
                if (_debug_launch_attempt > 120)
                {
                    if (_debug_launch_delay_timer != null)
                        _debug_launch_delay_timer.Stop();

                    if (_dlg_watcher != null)
                    {
                        _dlg_watcher.Invoke(new Action(delegate() { _dlg_watcher.Dispose(); }));
                        _dlg_watcher = null;
                    }

                    MessageBox.Show("The worker process could not be found. The debug process watcher will be terminated.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

                    return;
                }

                Debugger2 dbg2 = m_dte.Debugger as Debugger2;

                Transport trans = dbg2.Transports.Item(1);
                for(int i = 1; i <= dbg2.Transports.Count; i++)
                {
                    if (dbg2.Transports.Item(i).Name == _debug_transport)
                    {
                        trans = dbg2.Transports.Item(i);
                    }
                }

                Engine engine;
                if (_debug_transport == "Default")
                {
                    engine = trans.Engines.Item("Managed");
                }
                else
                {
                    engine = trans.Engines.Item(1);
                }

                Uri uri = new Uri(_debug_launch_target_url);
                string server;
                if (_debug_project != null)
                {
                    if (_debug_project.UseRemoteServer)
                    {
                        server = _debug_project.RemoteConnection.Address;
                    }
                    else
                    {
                        server = "localhost";
                    }
                }
                else
                {
                    server = uri.Host;
                }

                _debug_wp_process_id = _debug_launch_service_proxy.GetWebApplicationPID(_debug_launch_target_url);
                if (_debug_wp_process_id == -1)
                {
                    _debug_launch_delay_timer.Start();
                    return;
                }

                Processes processes = dbg2.GetProcesses(trans, server);

                if (processes.Count == 0)
                {
                    if (_debug_launch_delay_timer != null)
                        _debug_launch_delay_timer.Stop();

                    if (_dlg_watcher != null)
                    {
                        _dlg_watcher.Invoke(new Action(delegate() { _dlg_watcher.Dispose(); }));
                        _dlg_watcher = null;
                    }

                    throw new Exception("No processes could be found on server " + server + ". This could be due to a logon failure.");
                }

                foreach (Process2 p in processes)
                {
                    if (p.ProcessID == _debug_wp_process_id)
                    {
                        if (_debug_launch_delay_timer != null)
                            _debug_launch_delay_timer.Stop();

                        if (_dlg_watcher != null)
                        {
                            _dlg_watcher.Invoke(new Action(delegate() { _dlg_watcher.Dispose(); }));
                            _dlg_watcher = null;
                        }

                        if (p.IsBeingDebugged)
                        {
                            throw new Exception("The process is already being debugged.");
                        }
                        else
                        {
                            p.Attach2(engine);
                        }


                        return;
                    }
                }

            }
            catch (Exception ex)
            {
                DialogError.ShowError(ex, "Failed to attach debugger. For remote debugging please read the msdn article http://msdn.microsoft.com/en-us/library/y7f5zaaa.aspx.", false);

                try
                {
                    if (_debug_launch_delay_timer != null)
                        _debug_launch_delay_timer.Stop();

                    if (_dlg_watcher != null)
                    {
                        _dlg_watcher.Invoke(new Action(delegate() { _dlg_watcher.Dispose(); }));
                        _dlg_watcher = null;
                    }
                }
                catch { }
            }
        }

    }
}
